package com.quectel.cms.interceptor;

import com.quectel.cms.constants.CmsRedisCacheConstants;
import com.quectel.cms.constants.CmsSystemConstants;
import com.quectel.constant.global.annotation.RequirePermissions;
import com.quectel.constant.global.annotation.RequireRoles;
import com.quectel.constant.global.annotation.RequireTenants;
import com.quectel.sys.module.sys.service.SysRoleService;
import com.quectel.sys.module.sys.service.SysUserService;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.common.Response;
import com.quectel.util.redis.RedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * 在这里编写说明
 *
 * @author: CL
 * @email: louis.cao@quectel.com
 * @date: 2019-02-28 17:37:00
 */
@Component
public class LoginInterceptor implements AsyncHandlerInterceptor, WebMvcConfigurer {


    private static Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);

    @DubboReference
    private SysUserService sysUserService;
    @DubboReference
    private SysRoleService sysRoleService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            //请求的不是方法
            return true;
        }
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }
        if (StringUtils.isEmpty(token)) {
            //header和url都没有传入token
            writeError(response, Response.customize(CmsSystemConstants.TOKEN_IS_NULL_CODE, CmsSystemConstants.TOKEN_IS_NULL_MSG));
            return false;
        }


        //验证token是否过期
        String loginUserInfoStr = RedisUtils.get(CmsRedisCacheConstants.TOKEN_USER_PREFIX + token);
        if (StringUtils.isEmpty(loginUserInfoStr)) {
            writeError(response, Response.customize(CmsSystemConstants.TOKEN_EXPIRED_CODE, CmsSystemConstants.TOKEN_EXPIRED_MSG));
            return false;
        }

        LoginUserSession loginUserInfo = JacksonUtils.parse(loginUserInfoStr, LoginUserSession.class);

        HandlerMethod method = (HandlerMethod) handler;
        //验证是否拥有该权限
        RequirePermissions requirePermissions = method.getMethodAnnotation(RequirePermissions.class);
        if (requirePermissions != null && requirePermissions.values().length > 0) {
            int i = 0;
            for (i = 0; i < requirePermissions.values().length; i++) {
                if (loginUserInfo.getPermissions().contains(requirePermissions.values()[i])) {
                    break;
                }
            }
            if (i >= requirePermissions.values().length) {
                writeError(response, Response.customize(CmsSystemConstants.TOKEN_PRIVILEGE_NOT_ENOUGH_CODE, CmsSystemConstants.TOKEN_PRIVILEGE_NOT_ENOUGH_MSG));
                return false;
            }
        }

        //是否有该角色
        RequireRoles requireRoles = method.getMethodAnnotation(RequireRoles.class);
        if (requireRoles != null && requireRoles.values().length > 0) {

            int i = 0;
            for (i = 0; i < requireRoles.values().length; i++) {
                if (loginUserInfo.getUserRoles().contains(requireRoles.values()[i])) {
                    break;
                }
            }
            if (i >= requireRoles.values().length) {
                writeError(response, Response.customize(CmsSystemConstants.TOKEN_PRIVILEGE_NOT_ENOUGH_CODE, CmsSystemConstants.TOKEN_PRIVILEGE_NOT_ENOUGH_MSG));
                return false;
            }
        }

        //是否有该租户
        RequireTenants requireTenants = method.getMethodAnnotation(RequireTenants.class);
        if (requireTenants != null && requireTenants.values().length > 0) {
            if (!Arrays.asList(requireTenants.values()).contains(loginUserInfo.getTenantCode())) {
                writeError(response, Response.customize(CmsSystemConstants.TOKEN_PRIVILEGE_NOT_ENOUGH_CODE, CmsSystemConstants.TOKEN_PRIVILEGE_NOT_ENOUGH_MSG));
                return false;
            }
        }
        RedisUtils.expire(CmsRedisCacheConstants.TOKEN_USER_PREFIX + token, CmsRedisCacheConstants.TOKEN_DEFAULT_TIMEOUT, TimeUnit.MINUTES);
        RedisUtils.expire(CmsRedisCacheConstants.USER_TOKEN_PREFIX + loginUserInfo.getUserId(), CmsRedisCacheConstants.TOKEN_DEFAULT_TIMEOUT, TimeUnit.MINUTES);

        request.setAttribute(CmsSystemConstants.USER_SESSION_SAVE_KEY, loginUserInfo);
        return true;
    }


    private void writeError(HttpServletResponse response, Response result) {
        try {
            response.setHeader("Content-Type", "application/json");
            ServletOutputStream out = response.getOutputStream();
            out.write(JacksonUtils.toJsonString(result).getBytes("utf-8"));
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this)
                .addPathPatterns("/**")
                .excludePathPatterns("/ws")
                .excludePathPatterns("/welcome")
                .excludePathPatterns("/common/*")
                .excludePathPatterns("/smallFile/download")
                .excludePathPatterns("/smallFile/imgShow")
                .excludePathPatterns("/ueditor/**")
                .excludePathPatterns("/sys/login")
                .excludePathPatterns("/sys/logout")
                .excludePathPatterns("/sys/loginBigScreen")
                .excludePathPatterns("/sys/getPublicKey")
                //openapi
                .excludePathPatterns("/v3/api-docs/**", "/swagger-ui.html");

    }

}
